home *** CD-ROM | disk | FTP | other *** search
- /*
- * Copyright 1991, 1992, 1993, 1994, Silicon Graphics, Inc.
- * All Rights Reserved.
- *
- * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
- * the contents of this file may not be disclosed to third parties, copied or
- * duplicated in any form, in whole or in part, without the prior written
- * permission of Silicon Graphics, Inc.
- *
- * RESTRICTED RIGHTS LEGEND:
- * Use, duplication or disclosure by the Government is subject to restrictions
- * as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
- * and Computer Software clause at DFARS 252.227-7013, and/or in similar or
- * successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
- * rights reserved under the Copyright Laws of the United States.
- */
- /*
- * tabletglx.c : a mixed model (GL & X) tablet "line-drawing" demo program.
- * this program imitates a pen (stylus) drawing on a surface:
- * while the pen (stylus' button) is pressed down, a line
- * continues to be drawn. when the pen is released, the
- * current line stops.
- *
- * ratmandu -- fall 1991
- */
- #include <stdio.h>
- #include <stdlib.h>
- #include <X11/Xlib.h>
- #include <X11/Xutil.h>
- #include <X11/Xos.h>
- #include <X11/Xatom.h>
- #include <X11/extensions/XI.h>
- #include <X11/extensions/XInput.h>
- #include <gl/glws.h>
- #include <gl/gl.h>
-
-
- #define X 0
- #define Y 1
- #define MAXRESOLUTION 2206
-
- /* line drawing structures to create a new linked-list everytime the
- * stylus (button) is pressed down and grow that list as long as
- * MotionNotify events occur. when the stylus (button) is released,
- * free up the list and reset the head and tail to point to null
- */
- #define newll() ((struct lineElement *)malloc(sizeof(struct lineElement)))
-
- struct lineElement {
- long xy[2];
- struct lineElement *next;
- };
-
- typedef struct {
- struct lineElement *head, *tail;
- } LineList;
-
- LineList lineLs;
- struct lineElement *lineDummy;
-
- Display *dpy; /* The X server connection */
- Atom del_atom; /* WM_DELETE_WINDOW atom */
- Window glwin; /* handle to the GL window */
-
- static void openwindow(char *);
- static void setupdevs();
- static Window glx_create_window(Display*, Window, int, int, int, int, char *);
- static void makeframe();
- static void clean_exit(void);
-
-
- int tablet_device_id; /* device handles for the */
- int tablet_press_type; /* the following 3 tablet device ID handles are */
- int tablet_release_type; /* defined in setupdevs() and then used in the */
- int tablet_motion_type; /* infinite (get/process input) loop in main */
-
- float ratio; /* stores the current ratio to scale the tablet's full */
- /* range of coordinates into the window's current size */
- int xsize, ysize; /* stores window's current size values */
-
- void main(int argc, char *argv[])
- {
- int i;
- long xprev, yprev; /* used if either the x or y stylus val doesn't chng */
- int myExpose, myConfigure, myMotion,
- myButtPress, myButtRelease, myButtDown; /* store which events occur */
-
-
-
- myExpose = myConfigure = myMotion = FALSE;
- myButtPress = myButtRelease = myButtDown = FALSE;
-
- openwindow(argv[0]); /* open the "window" like winopen wud do */
- setupdevs(); /* make the necessary connections to the tablet */
-
- /*
- * The event loop.
- */
- while (1) { /* standard logic: get event(s), process event(s) */
-
- XEvent event;
- int axis_data[6];
-
- gflush(); /* For proper DGL performance */
-
- /* this "do while" loop does the `get events' half of the "get events,
- * process events" action of the infinite while. this is to ensure
- * the event queue is always drained before the events that have come
- * in are processed.
- */
- do {
-
- XNextEvent(dpy, &event);
- switch (event.type) {
-
- /* "Expose" events are sort of like "REDRAW" in gl-speak in
- * terms of when a window becomes visible, or a previously
- * invisible part becomes visible.
- */
- case Expose: /* Exposures */
- myExpose = TRUE;
- break;
-
-
- /* "ConfigNotify" events are like "REDRAW" in terms of changes
- * to a window's size or position.
- */
- case ConfigureNotify: /* Resize GL manually */
- /* save the changed width/height of the parent X window */
- xsize = event.xconfigure.width;
- ysize = event.xconfigure.height;
- ratio = (float) xsize / MAXRESOLUTION;
- myConfigure = TRUE;
- break;
-
- case ButtonRelease: /* Back door exit */
- if (event.xbutton.button == Button1)
- clean_exit();
- break;
-
- case ClientMessage: /* WM invoked exit */
- if (event.xclient.data.l[0] == del_atom)
- clean_exit();
- break;
-
- /* since interest is on the tablet, it becomes the default */
- default:
-
- if ((lineLs.tail != NULL) && /* make sure we've already
- processed a button (stylus)
- press event which sets up
- the linked-list for x/y
- pair storage/line drawing */
-
- (event.type == tablet_motion_type) && /* make sure
- this is a mo-
- tion event */
-
- myButtDown) { /* and make sure the button itself
- is still down--myButtPress only
- processes the occurence of the
- but press (which sets up the new
- linked list) and then is
- immediately reset to FALSE. */
-
- /* the body of this if statement processes "Motion" events
- * from any one of the dials. the axes_count element of
- * the XDeviceMotionEvent structure (defined in /usr/include
- * /X11/extensions/XInput.h) is used to determine if there
- * are 2 or only 1 new coordinate value(s):
- * if (axes_count == 2), both X and Y have changed,
- * if (axes_count == 1), only Y has changed--X has not, and
- * if (axes_count == 0), only X has changed--Y has not.
- */
- XDeviceMotionEvent *M = (XDeviceMotionEvent *) &event;
-
- if (M->axes_count != 2) { /* if x OR y didn't chng */
- xprev = lineLs.tail->xy[X]; /* axes_count < 2 so */
- yprev = lineLs.tail->xy[Y]; /* need to save prev */
- }
- lineDummy = newll(); /* alloc a new element */
- lineDummy->next = NULL; /* for current line */
- lineLs.tail->next = lineDummy; /* point tail to it */
- lineLs.tail = lineDummy;
-
- if (M->axes_count == 2) { /* if new x/y pair, */
-
- lineLs.tail->xy[X] = M->axis_data[X];
- lineLs.tail->xy[Y] = M->axis_data[Y];
-
- } else if (M->first_axis == 0) { /* elseif new X val */
-
- lineLs.tail->xy[X] = M->axis_data[0];
- lineLs.tail->xy[Y] = yprev;
-
- } else if (M->first_axis == 1) { /* elseif new Y val */
-
- lineLs.tail->xy[X] = xprev;
- lineLs.tail->xy[Y] = M->axis_data[0];
-
- }
- myMotion = TRUE;
-
- } else if (event.type == tablet_press_type) {
-
- XDeviceButtonEvent *P = (XDeviceButtonEvent *) &event;
-
- xprev = P->axis_data[0]; /* butt's been pressed so */
- yprev = P->axis_data[1]; /* now start to make a new */
- myButtPress = TRUE; /* line. this cur pnt'll */
- myButtDown = TRUE; /* be the "move to" coord */
-
- } else if (event.type == tablet_release_type) {
-
- myButtRelease = TRUE;
- myButtDown = FALSE;
- }
- break;
-
- } /* end switch (event.type) */
-
- } while (XPending(dpy)); /* end "do { } while".
- * XPending() is like qtest()--it only
- * tells you if there're any events
- * presently in the queue. it does not
- * disturb queue's contents in any way.
- */
-
- /* On an "Expose" event, redraw the affected window
- */
- if (myExpose) {
- makeframe(); /* draw the GL stuff */
- myExpose = FALSE; /* reset flag--queue now empty */
- }
-
- /* On a "ConfigureNotify" event, resize window (XMoveResizeWindow),
- * and then redraw contents.
- */
- if (myConfigure) {
- XMoveResizeWindow(dpy, glwin, 0, 0, xsize, ysize);
- XSync(dpy, False); /* Need before GL reshape */
- viewport(0, (short) (xsize - 1), 0, (short) (ysize - 1));
- makeframe();
- myConfigure = FALSE; /* reset flag--queue now empty */
- }
-
- /* a motion-type event (the next x/y pair was already saved up above)
- * means we're still drawing more along the current line.
- */
- if (myMotion) {
- drawcurrentline(); /* butt still pressed, pen still moving, */
- /* keep drawing at end of current line */
- myMotion = FALSE; /* reset flag--queue now empty */
- }
-
- /* a "button press"-type event means we're starting a new line so we
- * need to re-initialize/create our linked-list.
- */
- if (myButtPress) {
- XDeviceButtonEvent *B = (XDeviceButtonEvent *) &event;
-
- lineDummy = newll(); /* making a new line so start a */
- lineDummy->next = NULL; /* new list. point head and tail */
- lineLs.head = lineDummy; /* to it, and assign current new */
- lineLs.tail = lineDummy; /* point to "tail" of list */
- lineLs.tail->xy[X] = xprev; /* make our first point be what */
- lineLs.tail->xy[Y] = yprev; /* we saved up above */
- myButtPress = FALSE; /* reset flag--queue now empty */
- }
-
- /* a "button release"-type event means the current line is complete
- * so now we need to free up the current linked-list.
- */
- if (myButtRelease) {
- struct lineElement *ptr;
- for (ptr = lineLs.head; ptr->next != NULL; ptr = ptr->next) {
- free(ptr); /* "empty" our current list */
- }
- lineLs.head = NULL;
- lineLs.tail = NULL;
- myButtRelease = FALSE; /* reset flag--queue now empty */
- }
- }
- }
-
-
-
-
- /* openwindow -
- * establish connection to X server, get screen info, specify the
- * attributes we want the WM to try to provide, and create the GL window
- */
- static void openwindow(char *progname) {
-
- int scrnnum; /* X screen number */
- int xorig, yorig; /* window (upper-left) origin */
- Window top, windows[2];
- long scrnheight;
- XSizeHints Winhints; /* used to fix window size */
-
-
-
- /* Connect to the X server and get screen info */
- if ((dpy = XOpenDisplay(NULL)) == NULL) {
- fprintf(stderr, "%s: cannot connect to X server %s\n",
- progname, XDisplayName(NULL));
- exit(1);
- }
- scrnnum = DefaultScreen(dpy);
- scrnheight = DisplayHeight(dpy, scrnnum);
-
- /* define window (upper-left) origin coords */
- xorig = 50;
- yorig = 50;
- xsize = 500;
- ysize = 500;
-
- ratio = (float) xsize / MAXRESOLUTION; /* calculate ratio to scale
- full tablet into window */
-
- /* create the top level X window */
- top = XCreateSimpleWindow(dpy, RootWindow(dpy, scrnnum),
- xorig, yorig, xsize, ysize, 0, 0, 0);
-
- /* specify the values for the Window Size Hints we want to enforce: this
- * window's aspect ratio needs to stay at 1:1, constrain min and max
- * window size, and specify the initial origin and size of the window.
- */
- Winhints.x = xorig; /* specify desired upper-left corner origin */
- Winhints.y = yorig; /* of window so prog will place itself */
- Winhints.width = xsize; /* specify desired x/y size of window */
- Winhints.height = ysize;
- Winhints.min_width = xsize/4; /* define min and max */
- Winhints.max_width = scrnheight-1; /* width and height */
- Winhints.min_height = ysize/4;
- Winhints.max_height = scrnheight-1;
- Winhints.min_aspect.x = 1; /* keep aspect at a 1:1 ratio */
- Winhints.max_aspect.x = 1;
- Winhints.min_aspect.y = 1;
- Winhints.max_aspect.y = 1;
- /* set the corresponding flags */
- Winhints.flags = USPosition|USSize|PMaxSize|PMinSize|PAspect;
- XSetNormalHints(dpy, top, &Winhints);
-
-
- /* create a GL imaging window */
- if ((glwin = glx_create_window(dpy,top,0,0,xsize,ysize,progname)) == NULL) {
- fprintf(stderr, "%s: could not create GL window\n", progname);
- exit(1);
- }
-
- /* define string that will show up in the window title bar (and icon) */
- XStoreName(dpy, top, "tablet mixed model \"line drawing\" program");
-
- /* declare interest in events we want the window to process */
- XSelectInput(dpy,top,StructureNotifyMask|ButtonPressMask|ButtonReleaseMask);
- XSelectInput(dpy, glwin, StructureNotifyMask|ExposureMask);
-
- /* express interest in WM killing this app */
- if ((del_atom = XInternAtom(dpy, "WM_DELETE_WINDOW", True)) != None)
- XSetWMProtocols(dpy, top, &del_atom, 1);
-
- /* ensure the GL colormap is installed for this app */
- windows[0] = glwin;
- windows[1] = top;
- XSetWMColormapWindows(dpy, top, windows, 2);
-
- /* map the windows */
- XMapWindow(dpy, glwin);
- XMapWindow(dpy, top);
- }
-
-
-
-
- GLXconfig params[] = {{ 0, 0, 0 }};
-
- /* glx_create_window -- Create a singlebuffered, colorindex X window
- * suitable for GL imaging.
- *
- * Parameters: dpy - X display pointer
- * parent - parent window for the GL imaging window
- * x, y - window origin relative to parent
- * w, h - window width and height
- * progname - name of executable
- */
- static Window glx_create_window(Display* dpy, Window parent, int x, int y,
- int w, int h, char *progname)
- {
- GLXconfig *next, *retconfig;
- Colormap cmap = DefaultColormap(dpy, DefaultScreen(dpy));
- XVisualInfo* vis;
- XVisualInfo template;
- XSetWindowAttributes cwa;
- int nret;
- Window win;
-
-
-
- /* Get configuration data for a window based upon all the defaults */
- if ((retconfig = GLXgetconfig(dpy, DefaultScreen(dpy), params)) == NULL) {
- fprintf(stderr,"%s: hdw doesn't support that window type\n",progname);
- exit(1);
- }
-
- /*
- * Scan through config info, pulling info we need to create a window
- * that supports the rendering mode.
- */
- for (next = retconfig; next->buffer; next++) {
- if (next->buffer == GLX_NORMAL) {
- if (next->mode == GLX_COLORMAP) {
- cmap = next->arg;
- }
- else if (next->mode == GLX_VISUAL) {
- template.visualid = next->arg;
- template.screen = DefaultScreen(dpy);
- vis = XGetVisualInfo(dpy, VisualScreenMask | VisualIDMask,
- &template, &nret);
- }
- }
- }
-
- /* Create the window */
- cwa.colormap = cmap;
- cwa.border_pixel = 0;
- win = XCreateWindow(dpy,parent,x,y,w,h,0,vis->depth,
- InputOutput,vis->visual,CWColormap|CWBorderPixel,&cwa);
-
- /* Rescan config info, find window slot GLXgetconfig provided, fill it
- * in w/window we just created.
- */
- for (next = retconfig; next->buffer; next++) {
- if ((next->buffer == GLX_NORMAL) && (next->mode == GLX_WINDOW)) {
- next->arg = win;
- break;
- }
- }
-
- /* link to the GL */
- if (GLXlink(dpy, retconfig) < 0) {
- fprintf(stderr, "%s: could not link with the GL\n", progname);
- exit(1);
- }
-
- /* set GL imaging */
- if (GLXwinset(dpy, win) < 0) {
- fprintf(stderr, "%s: could not winset GL window\n", progname);
- exit(1);
- }
-
- return win;
- }
-
-
-
-
- /* setupdevs -
- *
- * establish a live connection to the tablet device.
- *
- * leverages off the "X11 Input Extension Library Specification"
- * document (you *shud* be able to locate the on-line public access
- * directory which contains all the files to print hard-copy of this
- * document under .../mit/doc/extensions/xinput). refer to
- * /usr/include/X11/extensions/{XI.h, XInput.h} for structures accessed.
- */
- static void setupdevs() {
-
- int i, ndevices;
- XDevice *tablet_device;
- XDeviceInfoPtr lp, list;
- int num_ext_event_classes;
- XEventClass ListOfEventClass[3];
- int tablet_press_class, tablet_release_class, tablet_motion_class;
-
-
-
- /* get a ptr to the list of all currently defined input devices */
- list = (XDeviceInfoPtr) XListInputDevices(dpy, &ndevices);
- if (!list) {
- fprintf(stderr,"XlistInputDevices failed to generate a devices list\n"); exit(1);
- }
-
- /* check out the /usr/people/4Dgifts/examples/devices/input/X/Xlist.c
- * program (which gets compiled into "xlist"). running it will list
- * all the currently available input devices on the machine xlist is
- * run on. there is a LOT that should be studied in the "input" subtree.
- */
- for (lp=list, i=0; i<ndevices; lp++, i++) {
- if (lp->use == IsXExtensionDevice && strcmp(lp->name,"tablet") == 0) {
- break; /* found the right one--now save the ptr (lp) to it */
- }
- }
- if (i == ndevices) {
- fprintf(stderr, "\"tablet\" device not found\n");
- exit(1);
- }
- tablet_device = XOpenDevice(dpy, lp->id); /* open the Tablet device */
- if (!tablet_device) {
- fprintf(stderr, "XOpenDevice failedfor \"tablet\" device\n");
- exit(1);
- }
- tablet_device_id = tablet_device->device_id;
-
- /* the following 3 macros determine the given event's type and class.
- * each macro is passed the structure that describes the device from
- * which input is desired.
- */
- DeviceButtonPress(tablet_device, tablet_press_type, tablet_press_class);
- DeviceButtonRelease(tablet_device, tablet_release_type, tablet_release_class);
- DeviceMotionNotify(tablet_device, tablet_motion_type, tablet_motion_class);
-
- ListOfEventClass[0]=tablet_press_class;
- ListOfEventClass[1]=tablet_release_class;
- ListOfEventClass[2]=tablet_motion_class;
- num_ext_event_classes = 3;
-
- /* XSelectExtensionEvent requests the server to send events that match
- * the events and devices described by the event list and that come
- * from the requested window.
- */
- XSelectExtensionEvent(dpy, glwin, ListOfEventClass, num_ext_event_classes);
-
- }
-
-
-
- /* draw the tablet's current line segment now that event queue is drained.
- */
- drawcurrentline()
- {
- struct lineElement *ptr;
- long vect[2];
-
- color(CYAN);
-
- bgnline();
- for (ptr = lineLs.head; ptr->next != NULL; ptr = ptr->next) {
- vect[0] = (long) (ptr->xy[0]*ratio);
- vect[1] = (long) (ptr->xy[1]*ratio);
- v2i(vect);
- }
- endline();
- }
-
-
-
- /* makeframe -- Draw the tablet "background" in the GL-X window
- */
- static void makeframe()
- {
- color(BLUE);
- clear();
- ortho2(-0.5, xsize-0.5, -0.5, ysize-0.5);
-
-
- color(YELLOW);
- cmov2i(5, 4);
- charstr("Use left mouse button to quit.");
- }
-
-
-
- /* clean_exit -- Clean up before exiting
- */
- static void clean_exit(void)
- {
- XCloseDisplay(dpy);
- exit(0);
- }
-